package util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * python工具
 */
public class PythonUtil {

    public static void main(String[] args) {
        // 解释器
        String exe = "python";
        // python脚本路径，需要使用绝对路径
        String command = "F:\\学习项目\\java-learn-demo\\java-tools\\src\\main\\java\\json\\parseJson.py";
        String sourceFilePath = "D:\\下载\\微信下载\\WeChat Files\\wxid_22\\FileStorage\\File\\2022-12\\组织机构 - 副本.json";
        String targetFilePath = sourceFilePath.substring(0, sourceFilePath.lastIndexOf(".")) + "_3.json";
        // 组合成一个字符串数组
        String[] cmdArr = new String[]{exe, command, sourceFilePath, targetFilePath};
        executePythonScript(cmdArr);
    }

    /**
     * 执行python脚本
     * @param cmdArr cmd命令数组
     */
    public static void executePythonScript(String[] cmdArr) {
        if (cmdArr == null || cmdArr.length < 2) {
            // 参数缺失
            return;
        }
        try {
            // 创建进程对象并调用命令行执行python脚本代码
            // java: 从内部类中访问本地变量pr; 需要被声明为最终类型
            final Process pr = Runtime.getRuntime().exec(cmdArr);
            // python中print到流中，因此java也从流中读取数据
            // 避免waitFor死锁，需要把读取放在waitFor()之前
            // https://www.ab62.cn/article/1672.html
            BufferedReader normalReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));

            // String line = null;
            // while ((line = normalReader.readLine()) != null) {
            //     System.out.println(line);
            // }
            // 输出的日志信息居然属于错误流(ErrorStream)？？？
            // [INFO] [1/4] Opening document...
            // while ((line = errorReader.readLine()) != null) {
            //     System.out.println(line);
            // }

            // readLine卡死
            // https://blog.csdn.net/jstlovely/article/details/121247764
            // Process.getInputStream()和Process.getErrorStream()分别返回Process的标准输出流和错误流，两个流如果处理不当，其缓冲区不能被及时清除而被塞满，则进程被阻塞
            // 两个流需要同时读取
            // 防止缓冲区满, 导致卡住
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    String line;
                    try (InputStreamReader in = new InputStreamReader(pr.getErrorStream(), "GBK"); // GBK 解决中文乱码
                         BufferedReader stderr = new BufferedReader(in);
                    ) {
                        while ((line = stderr.readLine()) != null) {
                            System.out.println("stderr:" + line);
                        }
                    } catch (Exception e) {

                    }

                }
            }.start();

            System.out.println("==============打印结果==============");
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    String line;
                    try (InputStreamReader in = new InputStreamReader(pr.getInputStream(), "GBK");
                         BufferedReader stdout = new BufferedReader(in);
                    ) {
                        while ((line = stdout.readLine()) != null) {
                            System.out.println("stdout:" + line);
                        }
                    } catch (Exception e) {

                    }
                }
            }.start();

            // 使当前正在运行的线程在需要时等待，直到由该Process对象表示的进程完成其终止为止
            // https://blog.csdn.net/cumubi7453/article/details/107795393
            // 返回流程的退出值，当返回值0时，它反映了流程的正常终止
            int exitCode = pr.waitFor(); // 2 stderr:(null): can't open file 'PdfToDocx.py': [Errno 2] No such file or directory
            System.out.println(exitCode);
            if (exitCode == 0) {
                System.out.println("脚本文件执行成功!");
            } else {
                System.out.println("脚本文件执行失败!");
            }
            // 关闭流
            errorReader.close();
            normalReader.close();
            pr.destroy();
            System.out.println("end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行python脚本
     * @param cmd cmd命令
     * @return 执行结果 false 失败，true 成功
     */
    public static boolean executeCmd(String cmd) {
        // 执行结果 false 失败，true 成功
        boolean res = false;
        if (cmd == null || cmd.trim().length() == 0) {
            // 参数缺失
            return false;
        }
        try {
            // 创建进程对象并调用命令行执行python脚本代码
            // java: 从内部类中访问本地变量pr; 需要被声明为最终类型
            final Process pr = Runtime.getRuntime().exec(cmd);
            // python中print到流中，因此java也从流中读取数据
            // 避免waitFor死锁，需要把读取放在waitFor()之前
            // https://www.ab62.cn/article/1672.html
            // BufferedReader normalReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            // BufferedReader errorReader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));

            // String line = null;
            // while ((line = normalReader.readLine()) != null) {
            //     System.out.println(line);
            // }
            // 输出的日志信息居然属于错误流(ErrorStream)？？？
            // [INFO] [1/4] Opening document...
            // while ((line = errorReader.readLine()) != null) {
            //     System.out.println(line);
            // }

            // readLine卡死
            // https://blog.csdn.net/jstlovely/article/details/121247764
            // Process.getInputStream()和Process.getErrorStream()分别返回Process的标准输出流和错误流，两个流如果处理不当，其缓冲区不能被及时清除而被塞满，则进程被阻塞
            // 两个流需要同时读取
            // 防止缓冲区满, 导致卡住
            System.out.println("==============打印结果 pr.getInputStream()==============");
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    String line;
                    // GBK 解决中文乱码

                    try (InputStream inputStream = pr.getInputStream();
                         InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "GBK");
                         BufferedReader stdout = new BufferedReader(inputStreamReader);) {
                        while ((line = stdout.readLine()) != null) {
                            System.out.println("stdout:" + line);
                        }
                    } catch (Exception e) {

                    }
                }
            }.start();

            System.out.println("==============打印结果 pr.getErrorStream()==============");
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    String line;
                    try (
                        InputStream inputStream = pr.getErrorStream();
                        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "GBK");
                        BufferedReader stderr = new BufferedReader(inputStreamReader);) {
                        while ((line = stderr.readLine()) != null) {
                            System.out.println("stderr:" + line);
                        }
                    } catch (Exception e) {

                    }

                }
            }.start();

            // 使当前正在运行的线程在需要时等待，直到由该Process对象表示的进程完成其终止为止
            // https://blog.csdn.net/cumubi7453/article/details/107795393
            // 返回流程的退出值，当返回值0时，它反映了流程的正常终止
            int exitCode = pr.waitFor(); // 2 stderr:(null): can't open file 'PdfToDocx.py': [Errno 2] No such file or directory
            System.out.println(exitCode);
            if (exitCode == 0) {
                res = true;
                System.out.println("脚本文件执行成功!");
            } else {
                System.out.println("脚本文件执行失败!");
            }
            // 关闭流
            // errorReader.close();
            // normalReader.close();
            pr.destroy();
            System.out.println("end");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

}
